home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #50 (Nov 89) / Inside Code / MeterMain.c < prev    next >
C/C++ Source or Header  |  1989-08-30  |  37KB  |  1,229 lines

  1. /* MeterMain.c - test rig for designing meters, illustrating the interaction
  2. between documents, windows, and controls
  3. by Ken Earle, 1989
  4. ThinkC 3.0 settings:
  5.     Options...; <MacHeaders>, Check Pointers, Require Protoypes
  6.     Set Project Type...; File Type APPL, Creator whatever
  7. Files:    MacTraps
  8.         Math
  9.         Meter.c        MeterMain.c
  10. Headers: <MacHeaders> <math.h> "Meter.h"
  11. Resources: none (What? Blasphemy!)
  12. */
  13.  
  14. #include "Meter.h"
  15. #define        APPLEID                15
  16. #define        FILEID                16
  17. #define        EDITID                17
  18. #define        QUIT                12
  19. #define        HSCROLL                1L    /* scroll bar ID */
  20. #define        VSCROLL                2L
  21. #define        SCROLLSIZE            15
  22. #define        DOCHEIGHT            720 /* 10" x 72 */
  23. #define        DOCWIDTH            576 /*8" x 72 */
  24. #define        MAXWINDOWWIDTH        (DOCWIDTH + SCROLLSIZE)
  25. #define        MAXWINDOWHEIGHT        (DOCHEIGHT + SCROLLSIZE)
  26. #define        DISPLAYBOXWIDTH        72 /* <= 0 removes the display
  27.                                 and disables DrawDisplayBox() */
  28.  
  29. /* display modes are used by DrawDisplayBox() */
  30. enum DisplayMode
  31.     {
  32.     forWindow, forMeter, forThumb, forDebug = 300 /* forces to 16-bit size */
  33.     };
  34. typedef enum DisplayMode DisplayMode;
  35.  
  36. /* useful globals */
  37. WindowRecord    wRecord1, wRecord2;
  38. RgnHandle        updateRgn;
  39. MenuHandle      appleMenu, fileMenu, editMenu;
  40. /* handy globals */
  41. Rect            dragRect = { 0, 0, 1024, 1024 };
  42. char            wTitle1[] = "\PRear Window";
  43. char            wTitle2[] = "\PFront Window";
  44. char            mTitle1[] = "\PFirst Meter";
  45. char            mTitle2[] = "\PSecond Meter";
  46. char            mTitle3[] = "\PThird Meter";
  47. char            mTitle4[] = "\PFourth Meter";
  48.  
  49. /* Prototypes for functions defined in this file */
  50. int            main(void);
  51. void        DoStandardInits(void);
  52. void        MakeMenus(void);
  53. Boolean        DoMenu(long);
  54. void        MakeTwoWindows(void);
  55. void        MakeTwoMeters(WindowPtr, int);
  56. void        DitherMeters(void);
  57. void        DoContent(WindowPtr, EventRecord*);
  58. void        UpdateContents(WindowPtr, Boolean);
  59. void        DrawDisplayBox(WindowPtr, void**, DisplayMode, int, int);
  60. void        DoZoomWindow (WindowPtr, int);
  61. void        DoGrowWindow(EventRecord*);
  62. void        DoCloseWindow(void);
  63. Boolean        AMeterWindow(WindowPtr);
  64. void        GetScrollHandles(WindowPtr, ControlHandle*, ControlHandle*);
  65. void        LocalToDoc(Point*);
  66. void         MeterProc(MeterHandle, int);
  67. pascal void ScrollProc(ControlHandle,int);
  68. void         ThumbControl(ControlHandle, Point);
  69. void        PutMetersOnClip(WindowPtr);
  70.  
  71. main()
  72.     {
  73.     int                part;
  74.     EventRecord        myEvent;
  75.     WindowPtr        whichWindow;
  76.     GrafPtr            savePort;
  77.     MeterHandle        mH;
  78.     ControlHandle    cH;
  79.     Rect            cRect; /* see "activateEvt" */
  80.     RgnHandle        tempRgn; /* see updateEvt */
  81.     Boolean            quitting = FALSE;
  82.     
  83.     DoStandardInits();
  84.     updateRgn = NewRgn();
  85.     MakeMenus();
  86.     MakeTwoWindows();
  87.     
  88.     do /* while not quitting */
  89.         {
  90.         do /* while no next event */
  91.             {
  92.             SystemTask();
  93.             DitherMeters();
  94.             } while (!GetNextEvent(everyEvent, &myEvent));
  95.         switch (myEvent.what)
  96.             {
  97.         case mouseDown:
  98.             switch (part = FindWindow( myEvent.where, &whichWindow )) 
  99.                 {
  100.             case inDesk: 
  101.                 SysBeep(2);
  102.             break;
  103.             case inMenuBar:
  104.                 quitting = DoMenu(MenuSelect(myEvent.where));
  105.             case inSysWindow:
  106.                 SystemClick( &myEvent, whichWindow );
  107.             break;
  108.             case inDrag:
  109.                 if (AMeterWindow(whichWindow))
  110.                     DragWindow( whichWindow, myEvent.where, &dragRect );
  111.             break;
  112.             case inZoomIn:
  113.             case inZoomOut:
  114.                 if (AMeterWindow(whichWindow))
  115.                     if (TrackBox(whichWindow, myEvent.where, part))
  116.                         DoZoomWindow (whichWindow, part);
  117.             break;
  118.             case inGrow:
  119.                 if (AMeterWindow(whichWindow))
  120.                     DoGrowWindow(&myEvent);
  121.             break;
  122.             case inGoAway:
  123.                 if (AMeterWindow(whichWindow))
  124.                     if (TrackGoAway (whichWindow, myEvent.where))
  125.                         DoCloseWindow();
  126.             break;
  127.  
  128.             case inContent:
  129.                 if (whichWindow != FrontWindow())
  130.                     SelectWindow(whichWindow);
  131.                 else 
  132.                     if (AMeterWindow(whichWindow))
  133.                         DoContent(whichWindow, &myEvent);
  134.             break;
  135.             default: 
  136.             break;
  137.                 } /* switch FindWindow */
  138.         break;
  139.         case keyDown:
  140.         case autoKey: 
  141.         break;
  142.         case activateEvt:
  143.             whichWindow = (WindowPtr)myEvent.message;
  144.             if (AMeterWindow(whichWindow))
  145.                 {
  146.                 mH = (MeterHandle)(((WindowPeek)whichWindow)->refCon);
  147.                 cH = ((WindowPeek)whichWindow)->controlList;
  148.                 if ( myEvent.modifiers & activeFlag ) 
  149.                     {
  150.                     SetPort(whichWindow);
  151.                     ClipRect(&(whichWindow->portRect));
  152.                     InvalRect(&(whichWindow->portRect));
  153.                     DrawGrowIcon(whichWindow);
  154.                     if (cH)
  155.                         {
  156.                         while (cH)
  157.                             {
  158.                             ShowControl(cH);
  159.                             cRect = (**cH).contrlRect;
  160.                             ValidRect(&cRect);
  161.                             cH = (**cH).nextControl;
  162.                             }
  163.                         }
  164.                     if (ValidMeter(mH))
  165.                         {
  166.                         while (mH)
  167.                             {
  168.                             /* activate meters */
  169.                             (**mH).meterHilite = 0;
  170.                             mH = (**mH).nextMeter;
  171.                             }
  172.                         }
  173.                     cRect = thePort->portRect;
  174.                     cRect.bottom -= SCROLLSIZE;
  175.                     cRect.right -= SCROLLSIZE;
  176.                     ClipRect(&cRect);
  177.                     } /* if activate */
  178.                 else 
  179.                     {
  180.                     GetPort(&savePort);
  181.                     SetPort(whichWindow);
  182.                     ClipRect(&(whichWindow->portRect));
  183.                     InvalRect(&(whichWindow->portRect));
  184.                     DrawGrowIcon(whichWindow);
  185.                     if (cH)
  186.                         {
  187.                         while(cH)
  188.                             {
  189.                             HideControl(cH);
  190.                             cH = (**cH).nextControl;
  191.                             }
  192.                         }
  193.                     if (ValidMeter(mH))
  194.                         {
  195.                         while (mH)
  196.                             {
  197.                             /* deactivate meters */
  198.                             (**mH).meterHilite = 255;
  199.                             mH = (**mH).nextMeter;
  200.                             }
  201.                         }
  202.                     SetPort(savePort);
  203.                     } /* else deactivate */
  204.                 } /* if our window */
  205.         break;
  206.         case updateEvt: 
  207.             whichWindow = (WindowPtr)myEvent.message;
  208.             if (AMeterWindow(whichWindow))
  209.                 {
  210.                 GetPort(&savePort);
  211.                 SetPort( whichWindow );
  212.                 ClipRect(&whichWindow->portRect);
  213.                 BeginUpdate( whichWindow );
  214.                     EraseRect(&whichWindow->portRect);
  215.                     DrawGrowIcon(whichWindow);
  216.                     DrawControls(whichWindow);
  217.                     DrawDisplayBox(whichWindow, 0L, forWindow,0,0);
  218.                     /* Take the scrolls out of updateRgn, because
  219.                     UpdateContents() sets the clip region to the
  220.                     (global) updateRgn. */
  221.                     CopyRgn(whichWindow->visRgn,updateRgn);
  222.                     tempRgn = NewRgn();
  223.                     SetRectRgn(tempRgn, whichWindow->portRect.left,
  224.                         whichWindow->portRect.bottom - SCROLLSIZE, 
  225.                         whichWindow->portRect.right, 
  226.                         whichWindow->portRect.bottom);
  227.                     DiffRgn(updateRgn, tempRgn, updateRgn);
  228.                     SetRectRgn(tempRgn, whichWindow->portRect.right - SCROLLSIZE,
  229.                         whichWindow->portRect.top, 
  230.                         whichWindow->portRect.right, 
  231.                         whichWindow->portRect.bottom - SCROLLSIZE);
  232.                     DiffRgn(updateRgn, tempRgn, updateRgn);
  233.                     DisposeRgn(tempRgn);
  234.                     /* Redraw the "true contents" of the window. */
  235.                     UpdateContents(whichWindow, (whichWindow == FrontWindow()));
  236.                 EndUpdate( whichWindow );
  237.                 SetPort(savePort);
  238.                 }
  239.         break;
  240.         default: 
  241.         break;
  242.             } /* switch myEvent.what */
  243.         } while (!quitting);
  244.     if (FrontWindow())
  245.         PutMetersOnClip(FrontWindow());
  246.     } /* end main() */
  247.  
  248. void        DoStandardInits()
  249.     {
  250.     
  251.     MoreMasters();
  252.     MoreMasters();
  253.     MoreMasters();
  254.     InitGraf(&thePort);
  255.     InitFonts();
  256.     FlushEvents(everyEvent, 0);
  257.     InitWindows();
  258.     InitMenus();
  259.     TEInit();
  260.     InitDialogs(0L);
  261.     InitCursor();
  262.     }/* end DoStandardInits() */
  263.  
  264. void        MakeMenus()
  265.     {
  266.  
  267.     appleMenu = NewMenu(APPLEID,"\P\24");    
  268.     AppendMenu(appleMenu,"\P(About nothing...;(-");
  269.     AddResMenu(appleMenu,'DRVR');
  270.     InsertMenu(appleMenu,0);
  271.  
  272.     fileMenu = NewMenu(FILEID,"\PFile");
  273.     AppendMenu(fileMenu,"\PNew;Open...;(-;Close;Save;Save as...;Revert;(-;Page Setup...;Print...;(-;Quit/Q");
  274.     InsertMenu(fileMenu,0);    
  275.  
  276.     editMenu = NewMenu(EDITID,"\PEdit");
  277.     AppendMenu(editMenu,"\PUndo/Z;(-;Cut/X;Copy/C;Paste/V;Clear;(-;Show Clipboard;Select All");
  278.     InsertMenu(editMenu,0);
  279.  
  280.     DrawMenuBar();
  281.  
  282.     } /* end MakeMenus() */
  283.  
  284. /* DoMenu returns TRUE when quit selected */
  285. Boolean        DoMenu(menuAndItem)
  286. long        menuAndItem;
  287.     {
  288.     int        theItem;
  289.     char    name[64];
  290.     
  291.     theItem = LoWord(menuAndItem);
  292.     switch (HiWord(menuAndItem))
  293.         {
  294.     case APPLEID:
  295.         if (theItem > 1)
  296.             {
  297.             if (FrontWindow())
  298.                 PutMetersOnClip(FrontWindow());
  299.             GetItem(appleMenu, theItem, name);
  300.             OpenDeskAcc(name);
  301.             }
  302.     break;
  303.     case FILEID:
  304.         if (theItem == QUIT)
  305.             return(TRUE);
  306.     break;
  307.     case EDITID:
  308.     break;
  309.     default:
  310.     break;
  311.         }
  312.     HiliteMenu(0);
  313.     return(FALSE);
  314.     } /* end DoMenu() */
  315.  
  316. /* Make two windows right off the bat, each with a couple of scroll bars
  317. and a couple of meters. The windows are sized to fit the screen, but limited
  318. to a maximum size of 8" by 10" to show the slight complications that arise when
  319. dealing with a fixed document size. The trick thereafter is to ensure that you
  320. never stray outside the document - see DoGrowWindow() etc */
  321. void        MakeTwoWindows()
  322.     {
  323.     int                i, topGap, winWidth, winHeight;
  324.     int                horRange, vertRange;
  325.     Rect            boundsRect, cRect, clpRect;
  326.     WindowPtr        wPtr;
  327.     ControlHandle    cH;
  328.     WStateData        *wsd;
  329.     
  330.     /* Old small screen, or something bigger?
  331.     topGap = menu bar height + 20 for the window frame */
  332.     if ((screenBits.bounds.bottom - screenBits.bounds.top) < 343)
  333.         topGap = MBarHeight + 20;
  334.     else
  335.         topGap = 60;
  336.     /* topGap + window height + all offsets(1 here) + 4(at the bottom) = screen height */
  337.     winHeight = ((screenBits.bounds.bottom - screenBits.bounds.top - topGap - 14) <= MAXWINDOWHEIGHT) ? 
  338.             (screenBits.bounds.bottom - screenBits.bounds.top - topGap - 14) : MAXWINDOWHEIGHT;
  339.     winWidth = ((screenBits.bounds.right - screenBits.bounds.left - 18) <= MAXWINDOWWIDTH) ? 
  340.             (screenBits.bounds.right - screenBits.bounds.left - 18) : MAXWINDOWWIDTH;
  341.     SetRect(&boundsRect, 4, topGap, 4 + winWidth, topGap + winHeight);
  342.     /* Scroll bar ranges: range = amount of document left unseen (honest!) */
  343.     vertRange = MAXWINDOWHEIGHT - (boundsRect.bottom - boundsRect.top);
  344.     vertRange = (vertRange > 0) ? vertRange : 0;
  345.     horRange = MAXWINDOWWIDTH - (boundsRect.right - boundsRect.left);
  346.     horRange = (horRange > 0) ? horRange : 0;
  347.     /* Create two windows, offset slightly */
  348.     for (i = 1; i <= 2; ++i)
  349.         {
  350.         if (i == 1)
  351.             wPtr = NewWindow(&wRecord1, &boundsRect, wTitle1, TRUE, 8,-1L,TRUE, 0L);
  352.         else
  353.             {
  354.             OffsetRect(&boundsRect, 10, 10);
  355.             wPtr = NewWindow(&wRecord2, &boundsRect, wTitle2, TRUE, 8,-1L,TRUE, 0L);
  356.             }
  357.         SetPort(wPtr);
  358.         ClipRect(&(thePort->portRect));
  359.         /* Adjust zoom rectangles since document size is fixed. */
  360.         wsd = (WStateData*)*(((WindowPeek)wPtr)->dataHandle);
  361.         wsd->stdState = boundsRect;
  362.         wsd->userState = boundsRect;
  363.         /* horizontal bar */
  364.         /* Note room is reserved at right of the bar for the
  365.         display box - see DrawDisplayBox(). */
  366.         cRect.top = thePort->portRect.bottom - SCROLLSIZE;
  367.         cRect.left = thePort->portRect.left - 1;
  368.         cRect.bottom = thePort->portRect.bottom + 1;
  369.         cRect.right = thePort->portRect.right - DISPLAYBOXWIDTH - 14;
  370.         cH = NewControl(thePort,&cRect,"\P",TRUE,0,0,horRange,scrollBarProc,HSCROLL);
  371.         if (horRange == 0)
  372.             HiliteControl(cH, 255);
  373.         /* vertical bar */
  374.         cRect.top = thePort->portRect.top - 1;
  375.         cRect.left = thePort->portRect.right - SCROLLSIZE;
  376.         cRect.bottom = thePort->portRect.bottom - 14;
  377.         cRect.right = thePort->portRect.right + 1;
  378.         cH = NewControl(thePort,&cRect,"\P",TRUE,0,0,vertRange,scrollBarProc,VSCROLL);
  379.         if (vertRange == 0)
  380.             HiliteControl(cH, 255);
  381.         clpRect = thePort->portRect;
  382.         clpRect.bottom -= SCROLLSIZE;
  383.         clpRect.right -= SCROLLSIZE;
  384.         ClipRect(&clpRect);
  385.         MakeTwoMeters(wPtr, i);
  386.         }
  387.     }/* end MakeTwoWindows() */
  388.  
  389. /* Create four meters, two in each window. Note that it is not necessary to
  390. carry around all the meterhandles as globals since they are attached to the 
  391. window's refCon field as a linked list by NewMeter(). */
  392. void        MakeTwoMeters(wPtr, which)
  393. WindowPtr        wPtr;
  394. int                which;
  395.     {
  396.     Rect            boundsRect;
  397.     MeterHandle        mH;
  398.     
  399.     if (which == 1)
  400.         {
  401.         boundsRect.top = 0;
  402.         boundsRect.left = 0;
  403.         boundsRect.bottom = 0;
  404.         boundsRect.right = 144; /* 2 inches wide */
  405.         mH = NewMeter(wPtr, &boundsRect, mTitle1, FALSE, 256L, 0L, 512L, 0L);
  406.         (**mH).meterVis = TRUE;
  407.         (**mH).meterHilite = 255;
  408.         boundsRect.left = 150;
  409.         boundsRect.right = 222; /* 1 inch wide */
  410.         mH = NewMeter(wPtr, &boundsRect, mTitle2, FALSE, 0L, -1000L, 1000L, 0L);
  411.         (**mH).meterVis = TRUE;
  412.         (**mH).meterHilite = 255;
  413.         }
  414.     else if (which == 2) /* the front window at first */
  415.         {
  416.         boundsRect.top = 10;
  417.         boundsRect.left = 50;
  418.         boundsRect.bottom = 0;
  419.         boundsRect.right = 158;
  420.         NewMeter(wPtr, &boundsRect, mTitle3, TRUE, 2000L, 0L, 4095L, 0L);
  421.         boundsRect.left = 204;
  422.         boundsRect.right = 384;
  423.         NewMeter(wPtr, &boundsRect, mTitle4, TRUE, 0L, -40950L, 40950L, 0L);
  424.         }
  425.     }/* end MakeTwoMeters() */
  426.  
  427. /* Shake the needle around the setpoint to see it move. Affects
  428. all meters in the front window. */
  429. void        DitherMeters()
  430.     {
  431.     static int        random = 0;
  432.     static long        oldVal = 0L;
  433.     long            val, setVal, posIncrement, newVal;
  434.     int                direction, docV, docH;
  435.     ControlHandle    cHH, cHV;
  436.     MeterHandle    mH = (MeterHandle)(((WindowPeek)thePort)->refCon);
  437.     
  438.     if (ValidMeter(mH))
  439.         {
  440.         Delay(0L, &newVal);
  441.         if (newVal - oldVal > 8L) /* time delay */
  442.             {
  443.             if (random > 0)
  444.                 direction = 1;
  445.             else if (random < 0)
  446.                 direction = -1;
  447.             else
  448.                 direction = 0;
  449.             oldVal = newVal;
  450.             /* get control handles */
  451.             GetScrollHandles(thePort, &cHH, &cHV);
  452.             /* find where we've scrolled to */
  453.             docV = GetCtlValue(cHV);
  454.             docH = GetCtlValue(cHH);
  455.             /* shift to the right location in the document */
  456.             SetOrigin(docH, docV);
  457.             OffsetRgn(thePort->clipRgn, docH, docV);
  458.             /* run through all the meters for this window */
  459.             while (ValidMeter(mH))
  460.                 {
  461.                 val = (**mH).needleValue;
  462.                 setVal = (**mH).meterValue;
  463.                 posIncrement = ((**mH).meterMax - (**mH).meterMin)/250L;
  464.                 newVal = val + (setVal - val)/10L + direction*posIncrement;
  465.                 SetNeedleValue(mH, newVal);
  466.                 mH = (**mH).nextMeter;
  467.                 }
  468.             SetOrigin(0, 0);
  469.             OffsetRgn(thePort->clipRgn, -docH, -docV);
  470.             ++random;
  471.             if (random >= 5)
  472.                 random = -5;
  473.             }
  474.         }
  475.     }/* end DitherMeters() */
  476.  
  477. /* Handle Mouse down in our window - can hit scroll bars or meters. */
  478. void        DoContent(theWindow, theEvent)
  479. WindowPtr    theWindow;
  480. EventRecord    *theEvent;
  481.     {
  482.     int                cntlCode, axis;
  483.     int                docV, docH;
  484.     int                oldMWidth, newMWidth;
  485.     Point            startPoint, endPoint;
  486.     ControlHandle    theCtl, cHH, cHV;
  487.     MeterHandle        theMeter;
  488.     Rect            limitRect, slopRect;
  489.     
  490.     startPoint = theEvent->where;
  491.     GlobalToLocal(&startPoint);
  492.     /* First handle scroll bar events... */
  493.     if ((cntlCode = FindControl(startPoint,theWindow,&theCtl)) != 0)
  494.         {
  495.         if (cntlCode >= inUpButton && cntlCode <= inPageDown) /* scroll, not thumb */
  496.             TrackControl(theCtl,startPoint,&ScrollProc);
  497.         else if (cntlCode == inThumb)
  498.             {
  499.             /* Ordinarily you would call TrackControl(theCtl,startPoint, 0L)
  500.             but today we're doing "real-time" thumb control. */
  501.             ThumbControl(theCtl, startPoint);
  502.             } /* else if in thumb */
  503.         } /* if in a control */
  504.     /* ...then handle "true content" mouse events. */
  505.     else 
  506.         {
  507.         /* We're going to treat the meters as being attached to the 
  508.         underlying document rather than the window frame, so we must shift to
  509.         "document" coordinates before determining which meter was hit. */
  510.         LocalToDoc(&startPoint);
  511.         if ((cntlCode = FindMeter(startPoint,theWindow,&theMeter)) != 0)
  512.             {
  513.             /* Shift everything to document coordinates before proceeding: */
  514.             /* 1 - get control handles */
  515.             GetScrollHandles(theWindow, &cHH, &cHV);
  516.             /* 2 - find where we've scrolled to */
  517.             docV = GetCtlValue(cHV);
  518.             docH = GetCtlValue(cHH);
  519.             /* 3 - shift to the right location in the document */
  520.             SetOrigin(docH, docV);
  521.             OffsetRgn(theWindow->clipRgn, docH, docV);
  522.             /* 4 - now deal with events as though no scrolling has
  523.             taken place */
  524.             if (theEvent->modifiers & optionKey)
  525.                 {
  526.                 /* Drag the meter around. This is very handy since meters take
  527.                 up a lot of screen. Unneeded meters can be slid almost entirely
  528.                 off the screen to make room for other things. */
  529.                 slopRect = limitRect = thePort->portRect;
  530.                 InsetRect(&limitRect, 10, 10);
  531.                 limitRect.right -= SCROLLSIZE;
  532.                 limitRect.bottom -=SCROLLSIZE;
  533.                 axis = 0; /* no constraint */
  534.                 DragMeter(theMeter, startPoint, &limitRect, &slopRect, axis);
  535.                 }
  536.             else if (theEvent->modifiers & cmdKey)
  537.                 {
  538.                 /* Resize the meter while mouse down - useful when playing
  539.                 with the look of the meter, and shows how the display box
  540.                 can be a very handy design tool. */
  541.                 oldMWidth = (**theMeter).meterRect.right - (**theMeter).meterRect.left;
  542.                 while (StillDown())
  543.                     {
  544.                     GetMouse(&endPoint); /* still in unscrolled coord's */
  545.                     newMWidth = oldMWidth - startPoint.h + endPoint.h;
  546.                     SizeMeter(theMeter, newMWidth);
  547.                     DrawDisplayBox(theWindow, (void**)theMeter, forMeter, 0,0);
  548.                     }
  549.                 }
  550.             else if (cntlCode >= inUpButton && cntlCode <= inPageDown) /* not background */
  551.                     TrackMeter(theMeter,startPoint,&MeterProc);
  552.             else if (cntlCode == inBackground)
  553.                 {
  554.                 /* default for inBackGround just shows set point value */
  555.                 TrackMeter(theMeter,startPoint,0L);
  556.                 }
  557.             SetOrigin(0, 0);
  558.             OffsetRgn(theWindow->clipRgn, -docH, -docV);
  559.             } /* if FindMeter */
  560.         } /* else true content */
  561.     } /* end DoContent() */
  562.  
  563. /* The all-purpose redraw function, draws only what touches
  564. the update region and handles scroll bars so that the routines
  565. which draw individual items don't have to keep track of where
  566. we've scrolled to in the document. Can even be used for printing,
  567. provided you temporarily set the scroll bar values to the minimum
  568. and the updateRgn to the whole document beforehand, and provided your
  569. individual drawing routines don't set the port before drawing. */
  570. void        UpdateContents(wPtr, frontMost)
  571. WindowPtr        wPtr;
  572. Boolean            frontMost;
  573.     {
  574.     int                docV, docH;
  575.     Rect            clpRect;
  576.     ControlHandle    cHH, cHV;
  577.     MeterHandle        mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
  578.     
  579.     SetClip(updateRgn);
  580.     /* get control handles */
  581.     GetScrollHandles(wPtr, &cHH, &cHV);
  582.     /* find where we've scrolled to */
  583.     docV = GetCtlValue(cHV);
  584.     docH = GetCtlValue(cHH);
  585.     /* shift to the right location in the document */
  586.     if (docV || docH)
  587.         {
  588.         SetOrigin(docH, docV);
  589.         OffsetRgn(wPtr->clipRgn, docH, docV);
  590.         }
  591.     /* we'll be ultra-efficient and draw only what
  592.     touches the updateRgn */
  593.     while (mH)
  594.         {
  595.         if (RectInRgn(&((**mH).meterRect), wPtr->clipRgn))
  596.             {
  597.             /* fool ShowMeter() into redrawing the meter */
  598.             (**mH).meterVis = FALSE;
  599.             ShowMeter(mH);
  600.             }
  601.         mH = (**mH).nextMeter;
  602.         }
  603.     if (docV || docH)
  604.         SetOrigin(0, 0);
  605.     clpRect.top = wPtr->portRect.top;
  606.     clpRect.left = wPtr->portRect.left;
  607.     clpRect.bottom = wPtr->portRect.bottom - SCROLLSIZE;
  608.     clpRect.right = wPtr->portRect.right - SCROLLSIZE;
  609.     ClipRect(&clpRect);
  610.     } /* end UpdateContents() */
  611.  
  612. /* Show two integer values in a little display box at bottom right
  613. of the window. If you call this function with mode = forDebug, specify
  614. the WindowPtr and put the two values in newValue and otherValue. */
  615. void        DrawDisplayBox(wPtr, theHandle, mode, newValue, otherValue)
  616. WindowPtr        wPtr;
  617. void            **theHandle;
  618. DisplayMode        mode;
  619. int                newValue, otherValue;
  620.     {
  621.     int                i, j;
  622.     int                oldFont, oldSize;
  623.     Style            oldStyle;
  624.     long            leftNum, rightNum;
  625.     char            s[20], sr[10], *sPtr;
  626.     Rect            displayRect, clpRect;
  627.     MeterHandle        mH;
  628.     ControlHandle    theCtl, cHH, cHV;
  629.     GrafPtr            savePort;
  630.     
  631.     if (DISPLAYBOXWIDTH <= 0) return;
  632.     GetPort(&savePort);
  633.     SetPort(wPtr);
  634.     oldFont = wPtr->txFont;
  635.     oldSize = wPtr->txSize;
  636.     oldStyle = wPtr->txFace; /* sic */
  637.     TextFont(0);
  638.     TextSize(12);
  639.     TextFace(' ');
  640.     displayRect.top = wPtr->portRect.bottom - SCROLLSIZE + 1;
  641.     displayRect.left = wPtr->portRect.right - DISPLAYBOXWIDTH -14;
  642.     displayRect.bottom = wPtr->portRect.bottom + 1;
  643.     displayRect.right = displayRect.left + DISPLAYBOXWIDTH - 1;
  644.     ClipRect(&displayRect);
  645.     EraseRect(&displayRect);
  646.     
  647.     if (mode == forWindow)
  648.         {
  649.         GetScrollHandles(wPtr, &cHH, &cHV);
  650.         leftNum = (long)GetCtlValue(cHH);
  651.         rightNum = (long)GetCtlValue(cHV);
  652.         /* or
  653.         leftNum = wPtr->portRect.right - wPtr->portRect.left - 15;
  654.         rightNum = wPtr->portRect.bottom - wPtr->portRect.top - 15;
  655.         */
  656.         }
  657.     else if (mode == forMeter)
  658.         {
  659.         mH = (MeterHandle)theHandle;
  660.         leftNum = (long)((**mH).meterRect.right - (**mH).meterRect.left);
  661.         rightNum = (long)((**mH).meterRect.bottom - (**mH).meterRect.top);
  662.         }
  663.     else if (mode == forThumb)
  664.         {
  665.         GetScrollHandles(wPtr, &cHH, &cHV);
  666.         theCtl = (ControlHandle)theHandle;
  667.         if (theCtl == cHH)
  668.             {
  669.             leftNum = (long)newValue;
  670.             rightNum = (long)GetCtlValue(cHV);
  671.             }
  672.         else
  673.             {
  674.             leftNum = (long)GetCtlValue(cHH);
  675.             rightNum = (long)newValue;
  676.             }
  677.         }
  678.     else if (mode == forDebug)
  679.         {
  680.         leftNum = (long)newValue;
  681.         rightNum = (long)otherValue;
  682.         }
  683.     else
  684.         {
  685.         leftNum = 0L;
  686.         rightNum = 0L;
  687.         }
  688.     NumToString(leftNum, s);
  689.     NumToString(rightNum, sr);
  690.     /* Concatenate sr into s with space between. */
  691.     j = s[0];
  692.     s[++j] = ' ';
  693.     for (i = 1, sPtr = &(s[j + 1]); i <= sr[0]; ++i)
  694.         *sPtr++ = sr[i];
  695.     s[0] += i; /* note 1 extra for space */
  696.     TextBox(&(s[1]), (Byte)(s[0]), &displayRect, teJustCenter);
  697.     
  698.     clpRect.top = thePort->portRect.top;
  699.     clpRect.left = thePort->portRect.left;
  700.     clpRect.bottom = thePort->portRect.bottom - SCROLLSIZE;
  701.     clpRect.right = thePort->portRect.right - SCROLLSIZE;
  702.     ClipRect(&clpRect);
  703.     TextFont(oldFont);
  704.     TextSize(oldSize);
  705.     TextFace(oldStyle);
  706.     SetPort(savePort);
  707.     } /* end DrawDisplayBox() */
  708.  
  709. /* Both DoZoomWindow() and DoGrowWindow() handle the potentially
  710. embarrassing problem of moving around in a document of fixed size
  711. without scrolling out into the Twilight Zone. */
  712. void        DoZoomWindow (wPtr, part)
  713. WindowPtr        wPtr;
  714. int             part;
  715.     {
  716.     int                oldV, oldH, newV, newH;
  717.     int                vertRange, horRange;
  718.     int                curValH, curMaxH, curValV, curMaxV;
  719.     Point            dS;
  720.     ControlHandle    cHH, cHV;
  721.     
  722.     /* get control handles */
  723.     GetScrollHandles(wPtr, &cHH, &cHV);
  724.     oldV = (wPtr->portRect).bottom - (wPtr->portRect).top;
  725.     oldH = (wPtr->portRect).right - (wPtr->portRect).left;
  726.     curValH = GetCtlValue(cHH);
  727.     curMaxH = GetCtlMax(cHH);
  728.     curValV = GetCtlValue(cHV);
  729.     curMaxV = GetCtlMax(cHV);
  730.     ZoomWindow(wPtr, part, TRUE);
  731.     InvalRect(&(wPtr->portRect));
  732.     newV = (wPtr->portRect).bottom - (wPtr->portRect).top;
  733.     newH = (wPtr->portRect).right - (wPtr->portRect).left;
  734.     SetRect(&((**cHV).contrlRect), wPtr->portRect.right - SCROLLSIZE, 
  735.         wPtr->portRect.top - 1, wPtr->portRect.right + 1, 
  736.         wPtr->portRect.bottom - 14);
  737.     SetRect(&((**cHH).contrlRect), wPtr->portRect.left - 1,
  738.         wPtr->portRect.bottom - SCROLLSIZE, wPtr->portRect.right - DISPLAYBOXWIDTH - 14, 
  739.         wPtr->portRect.bottom + 1);
  740.     vertRange = MAXWINDOWHEIGHT - (wPtr->portRect.bottom - wPtr->portRect.top);
  741.     vertRange = (vertRange > 0) ? vertRange : 0;
  742.     horRange = MAXWINDOWWIDTH - (wPtr->portRect.right - wPtr->portRect.left);
  743.     horRange = (horRange > 0) ? horRange : 0;
  744.     SetCtlMax(cHV, vertRange);
  745.     SetCtlMax(cHH, horRange);
  746.     if (vertRange == 0)
  747.         HiliteControl(cHV, 255);
  748.     else
  749.         HiliteControl(cHV, 0);
  750.     if (horRange == 0)
  751.         HiliteControl(cHH, 255);
  752.     else
  753.         HiliteControl(cHH, 0);
  754.     /* "Scroll" if running off the bottom or right. The whole window has
  755.     been invalidated above, so we just shift the scroll bars and
  756.     then let the update event take care of the redrawing later. */
  757.     /* dS.h(or .v) = overshoot = amount potentially exposed by zoom less
  758.     amount available to be exposed; potential amount = increase in
  759.     window size; amount available = maximum amount left unseen, which is
  760.     the (prezoom) control Max, less amount that has already scrolled by, 
  761.     which is the (prezoom) control Value. If you grok that, you've got 
  762.     scroll bars in the bag! */
  763.     dS.h = newH - oldH - curMaxH + curValH;
  764.     dS.h = (dS.h > 0) ? dS.h : 0;
  765.     dS.v = newV - oldV - curMaxV + curValV;
  766.     dS.v = (dS.v > 0) ? dS.v : 0;
  767.     if (dS.h > 0 || dS.v > 0)
  768.         {
  769.         SetCtlValue(cHV, GetCtlValue(cHV) - dS.v);
  770.         SetCtlValue(cHH, GetCtlValue(cHH) - dS.h);
  771.         }
  772.     } /* end DoZoomWindow() */
  773.  
  774. void        DoGrowWindow(event)
  775. EventRecord        *event;
  776.     {
  777.     int                winWidth, winHeight;
  778.     int                horRange, vertRange;
  779.     int                oldV, oldH, newV, newH;
  780.     int                curValH, curMaxH, curValV, curMaxV;
  781.     long            growResult;
  782.     Point            dS;
  783.     Rect            limitRect, badRect;
  784.     ControlHandle    cHH, cHV;
  785.     RgnHandle        tempRgn;
  786.     WindowPtr        wPtr = FrontWindow();
  787.     
  788.     winHeight = ((screenBits.bounds.bottom - screenBits.bounds.top - 22) <= MAXWINDOWHEIGHT) ? 
  789.             (screenBits.bounds.bottom - screenBits.bounds.top - 22) : MAXWINDOWHEIGHT;
  790.     winWidth = ((screenBits.bounds.right - screenBits.bounds.left - 2) <= MAXWINDOWWIDTH) ? 
  791.             (screenBits.bounds.right - screenBits.bounds.left - 2) : MAXWINDOWWIDTH;
  792.     limitRect.top = 130;
  793.     limitRect.left = 250;
  794.     limitRect.bottom = winHeight + 1;
  795.     limitRect.right = winWidth + 1;
  796.     /* get control handles */
  797.     GetScrollHandles(wPtr, &cHH, &cHV);
  798.     oldV = (wPtr->portRect).bottom - (wPtr->portRect).top;
  799.     oldH = (wPtr->portRect).right - (wPtr->portRect).left;
  800.     curValH = GetCtlValue(cHH);
  801.     curMaxH = GetCtlMax(cHH);
  802.     curValV = GetCtlValue(cHV);
  803.     curMaxV = GetCtlMax(cHV);
  804.     growResult = GrowWindow(wPtr, event->where, &limitRect);
  805.     newV = HiWord(growResult);
  806.     newH = LoWord(growResult);
  807.     SizeWindow(wPtr, newH, newV, TRUE);
  808.     ClipRect(&(wPtr->portRect));
  809.     SetRect(&((**cHV).contrlRect), wPtr->portRect.right - SCROLLSIZE, 
  810.         wPtr->portRect.top - 1, wPtr->portRect.right + 1, 
  811.         wPtr->portRect.bottom - 14);
  812.     SetRect(&((**cHH).contrlRect), wPtr->portRect.left - 1,
  813.         wPtr->portRect.bottom - SCROLLSIZE, wPtr->portRect.right - DISPLAYBOXWIDTH - 14, 
  814.         wPtr->portRect.bottom + 1);
  815.     vertRange = MAXWINDOWHEIGHT - (wPtr->portRect.bottom - wPtr->portRect.top);
  816.     vertRange = (vertRange > 0) ? vertRange : 0;
  817.     horRange = MAXWINDOWWIDTH - (wPtr->portRect.right - wPtr->portRect.left);
  818.     horRange = (horRange > 0) ? horRange : 0;
  819.     SetCtlMax(cHV, vertRange);
  820.     SetCtlMax(cHH, horRange);
  821.     if (vertRange == 0)
  822.         HiliteControl(cHV, 255);
  823.     else
  824.         HiliteControl(cHV, 0);
  825.     if (horRange == 0)
  826.         HiliteControl(cHH, 255);
  827.     else
  828.         HiliteControl(cHH, 0);
  829.     /* update scroll bars properly */
  830.     if (newV > oldV)
  831.         {
  832.         badRect.top = oldV - SCROLLSIZE;
  833.         badRect.left = 0;
  834.         badRect.bottom = oldV;
  835.         badRect.right = oldH;
  836.         InvalRect(&badRect);
  837.         }
  838.     else
  839.         {
  840.         badRect.top = (wPtr->portRect).bottom - SCROLLSIZE;
  841.         badRect.left = (wPtr->portRect).left;
  842.         badRect.bottom = (wPtr->portRect).bottom;
  843.         badRect.right = (wPtr->portRect).right;
  844.         InvalRect(&badRect);
  845.         }
  846.     if (newH > oldH)
  847.         {
  848.         badRect.top = 0;
  849.         badRect.left = oldH - SCROLLSIZE;
  850.         badRect.bottom = oldV;
  851.         badRect.right = oldH;
  852.         InvalRect(&badRect);
  853.         }
  854.     else
  855.         {
  856.         badRect.top = (wPtr->portRect).top;
  857.         badRect.left = (wPtr->portRect).right - SCROLLSIZE;
  858.         badRect.bottom = (wPtr->portRect).bottom;
  859.         badRect.right = (wPtr->portRect).right;
  860.         InvalRect(&badRect);
  861.         }
  862.     badRect.top = (wPtr->portRect).top;
  863.     badRect.left = (wPtr->portRect).left; 
  864.     badRect.bottom = (wPtr->portRect).bottom - SCROLLSIZE;
  865.     badRect.right = (wPtr->portRect).right - SCROLLSIZE;
  866.     /* Scroll if running off the bottom or right of document.
  867.     The newly exposed part is invalidated. The resulting
  868.     update event will set updateRgn equal to the total invalidated
  869.     region, and UpdateContents() will redraw only that part
  870.     of the window. */
  871.     dS.h = newH - oldH - curMaxH + curValH;
  872.     dS.h = (dS.h > 0) ? dS.h : 0;
  873.     dS.v = newV - oldV - curMaxV + curValV;
  874.     dS.v = (dS.v > 0) ? dS.v : 0;
  875.     if (dS.h > 0 || dS.v > 0)
  876.         {
  877.         ClipRect(&badRect);
  878.         tempRgn = NewRgn();
  879.         SetCtlValue(cHV, GetCtlValue(cHV) - dS.v);
  880.         SetCtlValue(cHH, GetCtlValue(cHH) - dS.h);
  881.         ScrollRect(&badRect, dS.h, dS.v, tempRgn);
  882.         InvalRgn(tempRgn);
  883.         DisposeRgn(tempRgn);
  884.         }
  885.     else
  886.         ClipRect(&badRect);
  887.     } /* end DoGrowWindow() */
  888.     
  889. void        DoCloseWindow()
  890.     {
  891.     WindowPtr    wPtr = FrontWindow();
  892.     
  893.     KillMeters(wPtr);
  894.     CloseWindow(wPtr); /* does KillControls() for you */
  895.     } /* end DoCloseWindow() */
  896.  
  897. Boolean        AMeterWindow(wPtr)
  898. WindowPtr        wPtr;
  899.     {
  900.     if (wPtr == (WindowPtr)(&wRecord1) || wPtr == (WindowPtr)(&wRecord2))
  901.         return(TRUE);
  902.     return(FALSE);
  903.     } /* end AMeterWindow() */
  904.  
  905. void        GetScrollHandles(wPtr, cH, cV)
  906. WindowPtr            wPtr;
  907. ControlHandle        *cV, *cH;
  908.     {
  909.     ControlHandle    cHH, cHV;
  910.     
  911.     cHH = ((WindowPeek)wPtr)->controlList;
  912.     while (cHH)
  913.         {
  914.         if (GetCRefCon(cHH) == HSCROLL) break;
  915.         cHH = (**cHH).nextControl;
  916.         }
  917.     cHV = ((WindowPeek)wPtr)->controlList;
  918.     while (cHV)
  919.         {
  920.         if (GetCRefCon(cHV) == VSCROLL) break;
  921.         cHV = (**cHV).nextControl;
  922.         }
  923.     *cH = cHH;
  924.     *cV = cHV;
  925.     } /* end AMeterWindow() */
  926.  
  927. void        LocalToDoc(thePt)
  928. Point        *thePt;
  929.     {
  930.     ControlHandle        cHH, cHV;
  931.     
  932.     /* get control handles */
  933.     GetScrollHandles(thePort, &cHH, &cHV);
  934.     /* Document coordinates are always at least as large
  935.     as local coord's - a mental picture of the window
  936.     sliding over the document helps.*/
  937.     thePt->v += GetCtlValue(cHV);
  938.     thePt->h += GetCtlValue(cHH);
  939.     } /* end LocalToDoc() */
  940.  
  941. void         MeterProc(mH, theCode)
  942. MeterHandle        mH;
  943. int                theCode;
  944.     {
  945.     long        posIncrement = ((**mH).meterMax - (**mH).meterMin)/250L;
  946.     long        scrollAmt;
  947.     
  948.     Delay(2L, &scrollAmt);
  949.     switch (theCode) 
  950.         {
  951.         case inUpButton: 
  952.             scrollAmt = posIncrement/10L;
  953.             if (!scrollAmt)
  954.                 scrollAmt = 1L;
  955.             break;
  956.         case inDownButton: 
  957.             scrollAmt = -posIncrement/10L;
  958.             if (!scrollAmt)
  959.                 scrollAmt = -1L;
  960.             break;
  961.         case inPageUp: 
  962.             scrollAmt = posIncrement;
  963.             break;
  964.         case inPageDown: 
  965.             scrollAmt = -posIncrement;
  966.             break;
  967.         }
  968.     SetMtrValue(mH, GetMtrValue(mH) + scrollAmt);
  969.     } /* end MeterProc() */
  970.  
  971. pascal void ScrollProc(theCtl, partCode)
  972. ControlHandle            theCtl;
  973. int                        partCode;
  974.     {
  975.     int                    delta, pageSize, arrowSize, oldValue;
  976.     Point                dS;
  977.     Rect                viewBnds;
  978.     Boolean                horizontal = FALSE;
  979.     
  980.     /* which control, how far */
  981.     if (GetCRefCon(theCtl) == HSCROLL)
  982.         {
  983.         horizontal = TRUE;
  984.         pageSize = (thePort->portRect).right - (thePort->portRect).left - SCROLLSIZE;
  985.         }
  986.     else /* VSCROLL */
  987.         pageSize = (thePort->portRect).bottom - (thePort->portRect).top - SCROLLSIZE;
  988.     arrowSize = pageSize/20;
  989.     /* delta is used to adjust local coord's, which DECREASE if 
  990.     up button pressed (we move closer to top of document).
  991.     However, we want the stuff in the window to slide DOWN
  992.     so the sign of dS is reversed from that of delta in
  993.     order that ScrollRect() will work correctly. Hence for
  994.     up button of vertical scroll, delta is negative and
  995.     dS.v is positive (they agree in magnitude unless
  996.     we're at one end or the other of the document).*/
  997.     switch (partCode)
  998.         {
  999.     case inUpButton:                
  1000.         delta = -arrowSize;
  1001.     break;
  1002.     case inDownButton:
  1003.         delta = arrowSize;
  1004.     break;
  1005.     case inPageUp:
  1006.         delta = -pageSize;
  1007.     break;
  1008.     case inPageDown:
  1009.         delta = pageSize;
  1010.     break;
  1011.     default:
  1012.         delta = 0;
  1013.     break;
  1014.         } /* end switch */
  1015.     ClipRect(&(thePort->portRect));
  1016.     oldValue = GetCtlValue(theCtl);
  1017.     SetCtlValue(theCtl, oldValue + delta);
  1018.     if (horizontal)
  1019.         {
  1020.         dS.h = oldValue - GetCtlValue(theCtl);
  1021.         dS.v = 0;
  1022.         }
  1023.     else /* vertical */
  1024.         {
  1025.         dS.v = oldValue - GetCtlValue(theCtl);
  1026.         dS.h = 0;
  1027.         }
  1028.     viewBnds.top = (thePort->portRect).top;
  1029.     viewBnds.left = (thePort->portRect).left;
  1030.     viewBnds.bottom = (thePort->portRect).bottom - SCROLLSIZE;
  1031.     viewBnds.right = (thePort->portRect).right - SCROLLSIZE;
  1032.     ScrollRect(&viewBnds, dS.h, dS.v, updateRgn);
  1033.     DrawDisplayBox(thePort, 0L, forWindow, 0,0);
  1034.     UpdateContents(thePort, TRUE);
  1035.     } /* end ScrollProc() */
  1036.  
  1037. /* This function will track the mouse and correctly update the value of a scroll bar as the
  1038. thumb is dragged around, in case you might want to update a graphic or number continuously
  1039. as the user moves the thumb about, something TrackControl() doesn't readily allow. Use
  1040. "ThumbControl(startPoint, theCtl);" in place of "if (TrackControl(theCtl,startPoint, 0L))"
  1041. when FindControl() reports an appropriate thumb event, and modify the action calls in this
  1042. routine where indicated. Depending on your needs, you may want to clip differently. For
  1043. generality, this routine decides which scroll bar is which by the same method that the
  1044. Control Manager uses - which way is longer? Much of "while (StillDown())" below is a
  1045. duplication of DragGrayRgn() that lets us tell where we are at all times.
  1046. */
  1047. void ThumbControl(theCtl, startPoint)
  1048. ControlHandle    theCtl;
  1049. Point            startPoint;
  1050.     {
  1051.     int            newValue, startValue, thickness;
  1052.     int            mouseRange, controlMin, controlMax;
  1053.     long        thumbRange, controlRange;
  1054.     Point        theMouse, thumbStart, thumbMin, thumbMax, newThumbCent, thumbCent, halfThumb;
  1055.     Rect        controlRect,slopRect, startRect, thumbRect;
  1056.     RgnHandle    oldClipRgn;
  1057.     PenState    pState;
  1058.     Boolean        isHorizontalBar, tracking;
  1059.     
  1060.     GetPenState(&pState);
  1061.     PenMode(patXor);
  1062.     PenPat(dkGray);
  1063.     PenSize(1,1);
  1064.     controlRect = (**theCtl).contrlRect;
  1065.     slopRect = controlRect;
  1066.     controlMin = GetCtlMin(theCtl);
  1067.     controlMax = GetCtlMax(theCtl);
  1068.     controlRange = (long)(controlMax - controlMin);
  1069.     startValue = GetCtlValue(theCtl);
  1070.     newValue = startValue;
  1071.     isHorizontalBar = (controlRect.right - controlRect.left) > 
  1072.                         (controlRect.bottom - controlRect.top) ? TRUE : FALSE;
  1073.     if (isHorizontalBar)
  1074.         {
  1075.         thumbStart.v = controlRect.top + (controlRect.bottom - controlRect.top + 1)/2;
  1076.         thumbMin.v = thumbMax.v = thumbStart.v;
  1077.         thickness = controlRect.bottom - controlRect.top;
  1078.         halfThumb.h = thickness/2;
  1079.         halfThumb.v = halfThumb.h - 1;
  1080.         thumbMin.h = controlRect.left + thickness + (thickness + 1)/2;
  1081.         thumbMax.h = controlRect.right - thickness - (thickness + 1)/2;
  1082.         thumbRange = (long)(controlRect.right - controlRect.left - 3*thickness);
  1083.         thumbStart.h = thumbMin.h + (thumbRange*(startValue - controlMin) + controlRange/2)/controlRange;
  1084.         InsetRect(&slopRect, -72, -36);
  1085.         }
  1086.     else
  1087.         {
  1088.         thumbStart.h = controlRect.left + (controlRect.right - controlRect.left + 1)/2;
  1089.         thumbMin.h = thumbMax.h = thumbStart.h;
  1090.         thickness = controlRect.right - controlRect.left;
  1091.         halfThumb.v = thickness/2;
  1092.         halfThumb.h = halfThumb.v - 1;
  1093.         thumbMin.v = controlRect.top + thickness + (thickness + 1)/2;
  1094.         thumbMax.v = controlRect.bottom - thickness - (thickness + 1)/2;
  1095.         thumbRange = (long)(controlRect.bottom - controlRect.top - 3*thickness);
  1096.         thumbStart.v = thumbMin.v + (thumbRange*(startValue - controlMin) + controlRange/2)/controlRange;
  1097.         InsetRect(&slopRect, -36, -72);
  1098.         }
  1099.     if (thumbRange <= 0) return; /* that's a pretty short scroll bar */
  1100.     newThumbCent = thumbCent = thumbStart;
  1101.     oldClipRgn = NewRgn();
  1102.     GetClip(oldClipRgn);
  1103.     ClipRect(&controlRect);
  1104.     SetRect(&thumbRect, thumbCent.h - halfThumb.h, thumbCent.v - halfThumb.v, 
  1105.         thumbCent.h + halfThumb.h, thumbCent.v + halfThumb.v);
  1106.     
  1107.     
  1108.     tracking = FALSE;
  1109.     while (StillDown())
  1110.         {
  1111.         ClipRect(&controlRect);
  1112.         GetMouse(&theMouse);
  1113.         if (PtInRect(theMouse, &slopRect))
  1114.             { /* mouse is close to scroll bar - track the thumb */
  1115.             /* calculate new thumb center */
  1116.             if (isHorizontalBar)
  1117.                 {
  1118.                 newThumbCent.h = thumbStart.h + (theMouse.h - startPoint.h);
  1119.                 if (newThumbCent.h < thumbMin.h)
  1120.                     newThumbCent.h = thumbMin.h;
  1121.                 else if (newThumbCent.h > thumbMax.h)
  1122.                     newThumbCent.h = thumbMax.h;
  1123.                 }
  1124.             else
  1125.                 {
  1126.                 newThumbCent.v = thumbStart.v + (theMouse.v - startPoint.v);
  1127.                 if (newThumbCent.v < thumbMin.v)
  1128.                     newThumbCent.v = thumbMin.v;
  1129.                 else if (newThumbCent.v > thumbMax.v)
  1130.                     newThumbCent.v = thumbMax.v;
  1131.                 }
  1132.             if (tracking)
  1133.                 {
  1134.                 if (newThumbCent.h != thumbCent.h || newThumbCent.v != thumbCent.v)
  1135.                     {
  1136.                     FrameRect(&thumbRect);
  1137.                     SetRect(&thumbRect, newThumbCent.h - halfThumb.h, newThumbCent.v - halfThumb.v, 
  1138.                         newThumbCent.h + halfThumb.h, newThumbCent.v + halfThumb.v);
  1139.                     FrameRect(&thumbRect);
  1140.                     }
  1141.                 }
  1142.             else /* just entering slop rect */
  1143.                 {
  1144.                 SetRect(&thumbRect, newThumbCent.h - halfThumb.h, newThumbCent.v - halfThumb.v, 
  1145.                     newThumbCent.h + halfThumb.h, newThumbCent.v + halfThumb.v);
  1146.                 FrameRect(&thumbRect);
  1147.                 tracking = TRUE;
  1148.                 }
  1149.             
  1150.             } /* if track the thumb */
  1151.         else /* not in slop rect */
  1152.             {
  1153.             if (tracking)
  1154.                 { /* just leaving slop rect */
  1155.                 FrameRect(&thumbRect);
  1156.                 tracking = FALSE;
  1157.                 newThumbCent = thumbStart;
  1158.                 }
  1159.             } /* else default to start position */
  1160.         if (newThumbCent.h != thumbCent.h || newThumbCent.v != thumbCent.v)
  1161.             {
  1162.             /* Either thumb has been dragged to new position or mouse has left
  1163.             slopRect, in which case thumbCent reverts to thumbStart. */
  1164.             /* Calculate control value based on newThumbCent. */
  1165.             newValue = (controlRange*(newThumbCent.h - thumbMin.h 
  1166.                 + newThumbCent.v - thumbMin.v) + thumbRange/2)/thumbRange + controlMin;
  1167.             thumbCent = newThumbCent;
  1168.             /*************************************************************************/
  1169.             /* Insert your custom call here - the current control value is "newValue";
  1170.             reset the clipRgn if you draw outside the scroll bar!*/
  1171.             
  1172.             /* Sample thumb action; echo control values to display box. */
  1173.             DrawDisplayBox(thePort, (void**)theCtl, forThumb, newValue,0);
  1174.             /*************************************************************************/
  1175.             }
  1176.         } /* while still down */
  1177.     if (tracking)
  1178.         FrameRect(&thumbRect);
  1179.     if (newValue != startValue)
  1180.         {
  1181.         SetCtlValue(theCtl, newValue);
  1182.         /* The control value has changed on mouseup  to "newValue" - take
  1183.         appropriate action - here we redraw the window */
  1184.         InvalRect(&(thePort->portRect));
  1185.         }
  1186.     else
  1187.         {
  1188.         /* the control value hasn't changed - reset if necessary */;
  1189.         }
  1190.     SetClip(oldClipRgn);
  1191.     DisposeRgn(oldClipRgn);
  1192.     SetPenState(&pState);
  1193.     } /* end ThumbControl() */
  1194.  
  1195. void        PutMetersOnClip(wPtr)
  1196. WindowPtr        wPtr;
  1197.     {
  1198.     Rect        theClipRect;
  1199.     PicHandle    clipPicH;
  1200.     RgnHandle    oldClipRgn;
  1201.     MeterHandle    mH = (MeterHandle)(((WindowPeek)wPtr)->refCon);
  1202.     
  1203.     ZeroScrap();
  1204.     theClipRect.top = 0;
  1205.     theClipRect.left = 0;
  1206.     theClipRect.bottom = DOCHEIGHT;
  1207.     theClipRect.right = DOCWIDTH;
  1208.     oldClipRgn = NewRgn();
  1209.     GetClip(oldClipRgn);
  1210.     ClipRect(&theClipRect);
  1211.     
  1212.     /* draw the picture */
  1213.     clipPicH = OpenPicture(&theClipRect);
  1214.     while (ValidMeter(mH))
  1215.         {
  1216.         MeterSnapshot(mH);
  1217.         mH = (**mH).nextMeter;
  1218.         }
  1219.     ClosePicture();
  1220.     /* put it on the scrap and clean up */
  1221.     HLock(clipPicH);
  1222.     PutScrap(GetHandleSize(clipPicH), 'PICT', *clipPicH);
  1223.     HUnlock(clipPicH);
  1224.     KillPicture(clipPicH);
  1225.     SetClip(oldClipRgn);
  1226.     DisposeRgn(oldClipRgn);
  1227.     } /* end PutMetersOnClip() */
  1228.  
  1229. /* End MeterMain.c */